package gateway

import (
	"bufio"
	"bytes"
	"context"
	"encoding/binary"
	"errors"
	"fmt"
	"github.com/spf13/cast"
	"io"
	"net"
	"server/component"
	"server/im/entity"
)

func NewTcpSocketServer(
	ctx context.Context,
	config component.ImServerConfig,
) GateWay {

	s := &TcpServer{BaseGateway: BaseGateway{
		ctx:               ctx,
		GatewayUni:        config.Uni,
		Config:            config,
		ClientMap:         make(map[string]*ClientItem),
		ClientChan:        make(chan entity.GatewayClientEvent),
		CoreInputChan:     make(chan entity.CoreInModel, 1024),
		CoreOutputChan:    make(chan entity.CoreOutModel, 1024),
		GatewayInputChan:  make(chan entity.GatewayInModel, 1024),
		GatewayOutputChan: make(chan entity.GatewayOutModel, 1024),
		BroadcastChan:     make(chan entity.GatewayBroadcastModel, 1024),
		exitChan:          make(chan struct{}),
	}}
	s.FnStartListen = s.doStartListen
	s.FnStopListen = s.doStopListen
	return s
}

type TcpServer struct {
	BaseGateway
	stopChan chan int
	listener net.Listener
}

func (o *TcpServer) doStopListen() {
	o.listener.Close()
	component.Log.Info(o.GatewayUni + "listen closed")
}

func (o *TcpServer) doStartListen() {
	port := o.Config.Port
	listener, err := net.Listen("tcp", ":"+port)
	if err != nil {
		panic(err)
	}
	o.listener = listener
	fmt.Printf("tcp %s listen on:%s \n", o.GetUniId(), port)

	for {
		conn, err := listener.Accept()
		if err != nil {
			if errors.Is(err, net.ErrClosed) {
				component.Log.Info("tcp " + o.GatewayUni + " listener stopped")
				return // 监听器关闭，退出协程
			} else {
				panic(err)
			}
		}
		socketConn := TcpSocketConn{
			conn:    conn,
			gateway: o,
		}
		o.Conn2Client(&socketConn)
	}
}

type TcpSocketConn struct {
	conn    net.Conn
	gateway GateWay
}

func (o *TcpSocketConn) GetGateWay() GateWay {
	return o.gateway
}

const (
	LEN_SIZE = 2
	CMD_SIZE = 2
)

func (o *TcpSocketConn) DoRead(readChan chan entity.GatewayInModel, errChan chan error, stopChan chan struct{}) {
	defer func() {
		fmt.Println("tcp read over")
		if err := recover(); err != nil {
			component.Log.Error(err)
		}
	}()
	bufReader := bufio.NewReader(o.conn)
	for {
		select {
		case <-stopChan:
			fmt.Println("tcp read break")
			return
		default:
			headerBytes, err := bufReader.Peek(LEN_SIZE)
			if err != nil {
				errChan <- err
				break
			}

			var dataLen uint16
			binary.Read(bytes.NewReader(headerBytes), binary.BigEndian, &dataLen)

			fmt.Println("msgLen", dataLen)

			if dataLen < LEN_SIZE {
				continue
			}

			dataBytes := make([]byte, dataLen)
			_, err = io.ReadFull(bufReader, dataBytes)
			if err != nil {
				errChan <- err
				break
			}

			bodyBytes := dataBytes[LEN_SIZE:]
			cmdBytes := bodyBytes[:CMD_SIZE]

			var cmd uint16
			binary.Read(bytes.NewReader(cmdBytes), binary.BigEndian, &cmd)

			msgBytes := bodyBytes[CMD_SIZE:]

			GatewayInModel := entity.GatewayInModel{
				Data: msgBytes,
				Cmd:  cmd,
				Ip:   o.conn.RemoteAddr().String(),
			}
			readChan <- GatewayInModel
		}
	}

}

func (o *TcpSocketConn) WriteFn(writeModel entity.GatewayOutModel) {

	msgBody := writeModel.Data

	dataLen := LEN_SIZE + CMD_SIZE + len(msgBody)
	dataLenUint16 := cast.ToUint16(dataLen)

	s1 := make([]byte, 0)
	msgBuff := bytes.NewBuffer(s1)
	err := binary.Write(msgBuff, binary.BigEndian, dataLenUint16)
	if err != nil {
		component.Log.Error(err)
		return
	}
	err = binary.Write(msgBuff, binary.BigEndian, writeModel.Cmd)
	if err != nil {
		component.Log.Error(err)
		return
	}
	msgBuff.Write(msgBody)

	s := msgBuff.Bytes()

	o.conn.Write(s)
}

func (o *TcpSocketConn) CloseFn() {
	o.conn.Close()
}
